//*********************************************************************
//		Copyright (c) 2003 - 2008 Microsoft Corporation.
//      This code is provided under Microsoft Public License, which can be found at
//      http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx 
//
// @File: Collector_EventLog.cs
//
// Purpose: Collector for NT Event Log events
//
//
//
// Notes: 
//
//
//	
// History: 
//     @Version: V2.1
//
//     07/14/08 PGV 2.1.001.0 Starting V2.1 (based on 2.0.030.0 code)
//
//
// @EndHeader@
//*********************************************************************

using System;
using System.Diagnostics;
using System.Collections;
using System.Data.SqlClient;
using System.Management;

using H2V2;

namespace H2V2.Collector
{
	public class CollectorEventLog : CollectorBase 
	{
		static int ms_collectorID = 0;
		const int WATSON_EVENT_DEBUG_LIMIT = 10;
		int MismatchedWatsonEvents = 0;

		ArrayList 
			m_app_event_array,
			m_sys_event_array;
		LogHistoryRecord [] m_log_arr = new LogHistoryRecord[2];
		LogSourceCollection m_source_col;


        public override string Version { get { return "2.2.001.0"; } }
		public override string Name { get { return "EventLog"; } }

		public CollectorEventLog (string a_machine_name, RunDescriptor a_rd)
			: base (a_machine_name, a_machine_name, a_rd)
		{
			// default collector mask is 0x7fffffff
			// 0x2 - System log; 0x4 - App log; 
			// 0x00010000 - Analyze Sys; 0x00020000 - Analyze App (SQL); 0x00040000 App (non-SQL)
			// m_options = 0x20004;
			m_app_event_array = new ArrayList (48);
			m_sys_event_array = new ArrayList (48);
			m_source_col = new LogSourceCollection ();
		}

		public override void Init (Connection a_repository) 
		{
			base.Init (a_repository);

			if (ms_collectorID == 0)
			{
				ms_collectorID = RegisterCollector ();
			}
			m_collectorID = ms_collectorID;
		}

		public void AddSource (string a_source, short a_inst_id, SqlServerVersion a_ver)
		{
			string name;

			name = CollectorBase.ConstructServiceName (a_source);

			LogSource source = new LogSource (name, a_inst_id, a_ver.H2level);
			m_source_col.Add (source);
		}

		public override void Collect ()
		{
			ChangeStatus ("Running EventLog collector on "+m_machine_name);

			UnitCollectSystem ();
			UnitCollectApplication ();
		}

		void UnitCollectSystem ()
		{
			if ((m_options & 0x10002) == 0) return;
			
			UnitOpen (1, "System Log");
			try
			{
				if ((m_options & 0x2) > 0) CollectSystem ();
				if ((m_options & 0x10002) > 0) AnalyzeOSoutage ();	// nothing to analyze if CollectSystem has not been run
				UnitSaveData ();
			}
			catch (CollectorErrorException cerr)
			{
				ChangeStatus (cerr);
			}
			catch (Exception ex)
			{
				ChangeStatus (ex.StackTrace, StatusEventType.STACK_TRACE);
				ChangeStatus (ex.Message,StatusEventType.ERROR);
				ChangeStatus ("Unit 1 failed", StatusEventType.ERROR);
				if (GlobalEnvironment.RepositoryFailure) throw;
			}
			finally
			{
				UnitClose ();
			}
		}

		void UnitCollectApplication ()
		{
			if ((m_options & 0x60004) == 0) return;

			LogSource source;

			UnitOpen (2, "Application Log");

			try
			{
				if ((m_options & 0x4) > 0) 
				{
					CollectApplication ();

					if ((m_options & 0x20000) > 0) 
					{
						for (int i=0; i<m_source_col.Count; i++)
						{
							source = (LogSource)m_source_col[i];
							ChangeStatus (" Instance: "+source.name);

							if (source.level == H2ProductLevel.PreSql2000SP3)
							{
								ChangeStatus (source.name+": HH supports SQL Server 2000 SP3 and higher only. Abandoning SQL events analysis", StatusEventType.WARNING);
							}
							else 
							{
								AnalyzeSqlOutages (source);
								AnalyzeTraceHistory (source);
								AnalyzeAV (source);
								StoreEventRegistry (source);
							}
						}
					}

					if ((m_options & 0x40000) > 0) 
					{
						AnalyzeWatson ();
					}
				}
				UnitSaveData ();
			}
			catch (CollectorErrorException cerr)
			{
				if (cerr.m_Error == CollectorError.AccessDenied) 
				{
					string msg = cerr.Message + " - Access Denied";
					throw (new CriticalCollectorErrorException (CollectorError.AccessDenied, msg));
				}
				else ChangeStatus (cerr);
			}
			catch (Exception ex)
			{
				ChangeStatus (ex.StackTrace, StatusEventType.STACK_TRACE);
				ChangeStatus (ex.Message,StatusEventType.ERROR);
				ChangeStatus ("Unit 2 failed", StatusEventType.ERROR);
				if (GlobalEnvironment.RepositoryFailure) throw;
			}
			finally
			{
				UnitClose ();
			}
		}

		#region System Log functions (CollectSystem, AnalyzeOSoutage)
		void CollectSystem ()
		{
			int i = 0,
				pcnt = 20,
				step = 20;
			EventLog Log;
			EventLogEntry lastEntry,
				entry;
			SystemLogEvent stev;

			ChangeStatus (" Opening System EventLog on "+m_machine_name);
			UnitUpdateStatus (1);

            if (!EventLog.Exists ("System", m_machine_name))
            {
                string msg = "Unable to find event log on " + m_machine_name;
                LogMgrError (RunErrorClass.EventLog, 0, "System Log", CollectorError.CannotFindEventLog, 0, msg);
                throw new CriticalCollectorErrorException (CollectorError.CannotFindEventLog, msg);
            }

			Log = new EventLog ("System", m_machine_name);
			try
			{
				if (Log.Entries.Count == 0) 
				{
					ChangeStatus ("  System Log is empty");
					return;
				}
			}
			catch
			{
                string msg = "Unable to access event log on "+m_machine_name;
                LogMgrError (RunErrorClass.EventLog, 0, "System Log", CollectorError.CannotAccessEventLog, 0, msg);
                throw new CriticalCollectorErrorException (CollectorError.CannotAccessEventLog, msg);
			}

			i = SetLogPosition (Log, 0);
			while (((i*100)/m_log_arr[0].count) >= pcnt) 
			{
				pcnt += step;
			} 
			lastEntry = Log.Entries[(i == 0 ? 0 : i-1)];

			for (;;) 
			{
				try
				{
					entry = Log.Entries[i];
					++i;
					lastEntry = entry;
				}
				catch (System.ArgumentException)
				{
					break;
				}

				if (entry.MachineName != this.m_machine_name.ToUpper ()) continue;

				if ((((i)*100)/m_log_arr[0].count) >= pcnt)
				{
					ChangeStatus ("  "+(i)+" ("+pcnt+"%) events processed");
					pcnt += step;
				}

				switch (entry.EventID)
				{
					case 6005:
					case 6006:
					case 6008:
					case 6009:
						stev = new SystemLogEvent (entry);
						break;
					default:
						continue;
				}

				m_sys_event_array.Add (stev);
			}
			ChangeStatus ("  "+(i)+" events processed", StatusEventType.INFO);

			m_log_arr[0].finish.index = lastEntry.Index;
			m_log_arr[0].finish.TimeGenerated = lastEntry.TimeGenerated;

			StoreLogHistory (0, m_log_arr[0]);
		}

		void AnalyzeOSoutage ()
		{
			int len = m_sys_event_array.Count,
				count = 0,
				iDirty = 0,
				last_eventID = 0,
				prev_eventID = 0;
			string version = ""; 
			bool MissedStop = false;
			DateTime last_start = MIN_DATE,
				last_stop = MIN_DATE;
			SystemLogEvent pev,
				cev;
			
			ChangeStatus ("  Analyzing OS outages");
			UnitUpdateStatus (2);

			last_start = GetLastBootTime(); 

			if (m_sys_event_array.Count == 0) return;

			cev = (SystemLogEvent)m_sys_event_array[0];

			foreach (SystemLogEvent stev in m_sys_event_array)
			{
				pev = cev;
				cev = stev;
				prev_eventID = last_eventID;
				last_eventID = stev.EventID;

				if (prev_eventID == last_eventID)
				{
					LogUnexpectedSystemEvent (pev);
					LogUnexpectedSystemEvent (cev);
					throw new CollectorErrorException (CollectorError.LogRepeatingSystemEvents, 0, "OS_outage_analyze - log inconsistency");
				}

				switch (last_eventID)
				{
					case 6006:
						last_stop = stev.TimeGenerated;
						iDirty = 0;
						MissedStop = false; 
						break;
					case 6008:
						if (last_eventID != 6006 || last_stop >= stev.TimeReferenced)
						{
							last_stop = stev.TimeReferenced;
						}
						iDirty = 1;
						break;
					case 6009:
						++count;
						version = stev.version;
						if (prev_eventID != 6006) MissedStop = true;
						break;
					case 6005:
						if (last_start > MIN_DATE)
						{
							if (MissedStop) 
							{
								last_stop = stev.TimeGenerated.AddSeconds (-1);
								MissedStop = false;
							}
							StoreOSoutage (last_start, last_stop, 1, version, iDirty);
						}
						last_start = stev.TimeGenerated;
						if (last_stop > MIN_DATE)
						{
							StoreOSoutage (last_stop, last_start, 0, version, iDirty);
						}
						break;
				}
			}
		}

		#endregion

		#region Subsidiary functions (pid, dbg_)
		static public string ParsePidFromEvent17104 (string a_str)
		{
			int i = a_str.Length-1;
			int first, last;

			while (!char.IsDigit(a_str, i) && i > 0)
			{--i;}
			last = i;

			while (char.IsDigit(a_str, i) && i >= 0)
			{--i;}
			first = i+1;

			return (a_str.Substring(first, last-first+1));
		}

		void LogUnexpectedEvent (EventLogEntry a_entry)
		{
			string msg;

			msg = a_entry.EventID+" - "+a_entry.TimeGenerated+" - "+a_entry.Index;
			foreach (string rs in a_entry.ReplacementStrings)
			{
				msg += " - "+rs;
			}

			ChangeStatus (msg, StatusEventType.INFO);
		}

		void LogUnexpectedSystemEvent (SystemLogEvent a_sysev)
		{
			string msg;

			msg = a_sysev.EventID+" - "+a_sysev.TimeGenerated;

			ChangeStatus (msg, StatusEventType.INFO);
		}

		void LogUnexpectedSqlEvent (SqlServerEvent a_sqlev)
		{
			string msg;

			msg = a_sqlev.inst_id+" - "+a_sqlev.EventID+" - "+a_sqlev.SubEventID+" - "+a_sqlev.pid+" - "
				+a_sqlev.TimeGenerated+" - "+a_sqlev.TimeReferenced;

			ChangeStatus (msg, StatusEventType.INFO);
		}

        /// <summary>
        /// This method tries to ensure that given event was logged by Watson
        /// It excludes known frequently found sources, using the same EventId
        /// it verifies Event signature, based on number of arguments
        /// There is no 100% guarantee of accurate detection
        /// </summary>
        /// <param name="entry"></param>
        /// <returns></returns>
		bool CheckWatsonEvent (EventLogEntry entry)
		{
			bool ret = false,
				debug = true;
			string src;

			switch (entry.EventID)
			{
				case 1000:
				case 1002:
				case 1004:
					src = entry.Source.ToUpper (System.Globalization.CultureInfo.InvariantCulture);
					if (src != "USERENV" && !src.StartsWith ("PERF") && src != "MSIINSTALLER" 
						&& src != "LOADPERF" && src != "USERINIT" && src !="WINLOGON"
						&& !src.StartsWith ("FRONTPAGE"))
					{
						if (entry.ReplacementStrings.Length == 5)
						{
                            try
                            {
                                Convert.ToInt32 (entry.ReplacementStrings[4], 16);
                                ret = true;
                                debug = false;
                            }
                            catch (System.ArgumentException) { }
                            catch (System.OverflowException) { }
                            catch (System.FormatException) { }
						}
						else if (entry.EventID == 1000 && src == "Microsoft Operations Manager")
						{
							ret = true;
							debug = false;
						}
					}
					else debug = false;
					break;
				case 1001:
					src = entry.Source.ToUpper ();
					if (src != "SCECLI" && src != "LOADPERF" && src != "MSIINSTALLER" 
						&& src != "USERINIT" && src != "WINLOGON")
					{
						if (entry.ReplacementStrings.Length == 1)
						{
							try
							{
								Convert.ToInt32 (entry.ReplacementStrings[0]);
								ret = true;
								debug = false;
							}
                            catch (System.ArgumentException) { }
                            catch (System.OverflowException) { }
                            catch (System.FormatException) { }
                        }
						else if (src == "Microsoft Operations Manager")
						{
							ret = true;
							debug = false;
						}
					}
					else debug = false;
					break;
				case 5000:
				case 5001:
					src = entry.Source.ToUpper ();
					if (src != "PASSPORTMANAGER")
					{
						if (entry.ReplacementStrings.Length == 11 || entry.ReplacementStrings.Length == 13)
						{
							ret = true;
							debug = false;
						}
					}
					else debug = false;
					break;
			}

			if (debug)
			{
				if (MismatchedWatsonEvents < WATSON_EVENT_DEBUG_LIMIT)
				{
					ChangeStatus ("EVENT: "+entry.EventID+", "+entry.Source+", "+entry.Message, StatusEventType.DEBUG);
					MismatchedWatsonEvents++;
				}
			}

			return ret;
		}
		#endregion

		int SetLogPosition (EventLog a_Log, int a_idx)
		{
			int i = 0,
				jump = 0;
			EventLogEntry firstEntry,
				entry;
			bool bScroll;
			string s_dbg = "";

			GetCutoffDate (a_idx);

			try
			{
				m_log_arr[a_idx].count = a_Log.Entries.Count;
				firstEntry = (EventLogEntry)a_Log.Entries[0];
				m_log_arr[a_idx].start.index = firstEntry.Index;
				m_log_arr[a_idx].start.TimeGenerated = firstEntry.TimeGenerated;
				if (!m_log_arr[a_idx].NoHistory)
				{
					m_log_arr[a_idx].IsGap = (m_log_arr[a_idx].cutoff.TimeGenerated < m_log_arr[a_idx].start.TimeGenerated);
				}
			}
			catch (Exception ex)
			{
                LogMgrError (RunErrorClass.EventLog, a_idx, ex.GetType ().Name, CollectorError.CannotReadLogEntry, 0, ex.Message);
				ChangeStatus (ex.GetType ().ToString ()+": "+ex.Message, StatusEventType.DEBUG);
                throw new CriticalCollectorErrorException (CollectorError.CannotReadLogEntry, "Unable to access event log on " + m_machine_name, ex);
			}
			ChangeStatus ("  "+m_log_arr[a_idx].count+" events in the log");

			bScroll = (!m_log_arr[a_idx].NoHistory && !m_log_arr[a_idx].IsGap);

			if (bScroll) 
			{
				if (H2V2.Constants.JumpLog)
				// ----------------------------------------------------------------------------------
				// !!! JUMP !!! ---------------------------------------------------------------------
                // Incremental Log collection - SQLH2 remembers the last event it read
                // It attempts to move to the next event without reading the all preceding events
                // (reading events is the most time consuming operation for remote machines)
                // The algorithm accounts for some potentintial problems due to volatile nature of the logs
				// >>
				{
					jump = m_log_arr[a_idx].cutoff.index - firstEntry.Index;
					
					// machine was reimaged or ??? (there is no Gap, but cutoff.index does not exist anymore)
					if (jump > m_log_arr[a_idx].count)
					{
                        LogMgrError (RunErrorClass.EventLog, a_idx, "Event Log", CollectorError.CannotPositionLog, 0, "There is no gap in history, but the previous last event is out of bounds");
                        throw new CollectorErrorException (CollectorError.CannotPositionLog, 0, "There is no gap in history, but the previous last event is out of bounds");
					}

					ChangeStatus ("LOG JUMP: "+firstEntry.Index+", "+m_log_arr[a_idx].cutoff.index+", "+jump, StatusEventType.DEBUG);

					entry = a_Log.Entries[jump];
					if (entry.TimeGenerated == m_log_arr[a_idx].cutoff.TimeGenerated && entry.Index == m_log_arr[a_idx].cutoff.index)
					{
						i = jump + 1;
					}
					else if (entry.TimeGenerated <= m_log_arr[a_idx].cutoff.TimeGenerated && entry.Index != m_log_arr[a_idx].cutoff.index)
					{ // Scroll Up
						s_dbg = "Scroll Up ";
						i = jump + 1;
						do
						{
							entry = a_Log.Entries[i];
							++i;
						} while (entry.TimeGenerated <= m_log_arr[a_idx].cutoff.TimeGenerated && entry.Index != m_log_arr[a_idx].cutoff.index);
					}
					else
					{ // Scroll Down
						s_dbg = "Scroll Down ";
						i = jump;
						do
						{
							--i;
							entry = a_Log.Entries[i];
						} while (entry.TimeGenerated > m_log_arr[a_idx].cutoff.TimeGenerated && entry.Index != m_log_arr[a_idx].cutoff.index);
						++i;
					}
			
					ChangeStatus ("  "+i+" ("+((i*100)/m_log_arr[a_idx].count)+"%) rolled forward");

					if (entry.TimeGenerated != m_log_arr[a_idx].cutoff.TimeGenerated || entry.Index != m_log_arr[a_idx].cutoff.index)
					{
						ChangeStatus ("Log positioning algorithm resulted in non-matching event (index or time)", StatusEventType.WARNING);
						ChangeStatus ("JUMP "+s_dbg+"ended on Index="+entry.Index+" time='"+entry.TimeGenerated+"'; "
							+" cutoff Index="+m_log_arr[a_idx].cutoff.index+" time='"+m_log_arr[a_idx].cutoff.TimeGenerated+"'",
							StatusEventType.DEBUG);
                        LogMgrError (RunErrorClass.EventLog, a_idx, "Event Log", CollectorError.LogPositionDoesntMatch, 0, "Log positioning algorithm resulted in non-matching event (index or time)");
					}

				}

				// <<
				// !!! JUMP !!! ---------------------------------------------------------------------
				// ----------------------------------------------------------------------------------

				else
				{
					do
					{
						try
						{
							entry = a_Log.Entries[i];
							++i;
						}
						catch (System.ArgumentException argex)
						{
							ChangeStatus (argex.Message, StatusEventType.DEBUG);
							break;
						}
					}
					while ((entry.TimeGenerated <= m_log_arr[a_idx].cutoff.TimeGenerated && entry.Index != m_log_arr[a_idx].cutoff.index)); 

					ChangeStatus ("  "+i+" ("+((i*100)/m_log_arr[1].count)+"%) rolled forward");
				}
			}

			return i;
		}


		#region CollectApplication 
		void CollectApplication ()
		{
			int i = 0,
				pcnt = 20,
				step = 20;
			LogSource lSource;
			EventLog Log;
			EventLogEntry lastEntry,
				entry;

			ChangeStatus (" Opening Application EventLog on "+m_machine_name);
			UnitUpdateStatus (3);
			
			if (!EventLog.Exists ("Application", m_machine_name))
			{
                string msg = "Unable to find event log on "+m_machine_name;
                LogMgrError (RunErrorClass.EventLog, 1, "Application Log", CollectorError.CannotFindEventLog, 0, msg);
                throw new CriticalCollectorErrorException (CollectorError.CannotFindEventLog, msg);
			}

			Log = new EventLog ("Application", m_machine_name);
			try
			{
				if (Log.Entries.Count == 0) 
				{
					ChangeStatus ("  Application Log is empty");
					return;
				}
			}
			catch (Exception ex)
			{
                LogMgrError (RunErrorClass.EventLog, 1, ex.GetType ().Name, CollectorError.CannotAccessEventLog, 0, ex.Message);
                throw new CriticalCollectorErrorException (CollectorError.CannotAccessEventLog, "Unable to access event log on " + m_machine_name, ex);
			}

			i = SetLogPosition (Log, 1);
			while (((i*100)/m_log_arr[1].count) >= pcnt) 
			{
				pcnt += step;
			} 
			lastEntry = Log.Entries[(i == 0 ? 0 : i-1)];

			for (;;) 
			{
				try
				{
					entry = Log.Entries[i];
					++i;
					lastEntry = entry;
				}
				catch (System.ArgumentException)
				{
					break;
				}

				if (entry.MachineName != this.m_machine_name.ToUpper ()) continue;

				if (entry.Source.StartsWith ("MSSQL") && (m_options & 0x20000) > 0)
				{ 
					// SQL event
					// events routed to version-dependent functions
					lSource = m_source_col.GetSource (entry.Source);
					if (lSource == null) continue;  // ASSERT ?

					c_event(entry, lSource);
				}
				else
				{
					switch (entry.EventID)
					{
						case 1000:	//Watson
						case 1001: 
						case 1002:
						case 1004:
						case 5000:
						case 5001:
							if (CheckWatsonEvent (entry))
							{
								m_app_event_array.Add (new AppLogEvent (entry));
							}
							break;
					}
				}
				
				if ((((i)*100)/m_log_arr[1].count) >= pcnt)
				{
					ChangeStatus ("  "+(i)+" ("+pcnt+"%) events processed");
					pcnt += step;
				}

			}
			ChangeStatus ("  "+(i)+" events processed", StatusEventType.INFO);

			m_log_arr[1].finish.index = lastEntry.Index;
			m_log_arr[1].finish.TimeGenerated = lastEntry.TimeGenerated;

			StoreLogHistory (1, m_log_arr[1]);
		}

		void c_event (EventLogEntry entry, LogSource lSource)
		{
			SqlServerEvent stev;
			string sub;
			int isub;
			short inst_id;

			inst_id = lSource.inst_id;

			switch (entry.EventID)
			{
				case 17176:
				case 17177:
					try 
					{
						stev = new SqlServerEvent (inst_id, entry, 0);
						stev.level = lSource.hlevel;
						if (lSource.hlevel == H2ProductLevel.Sql2005)
						{
							lSource.addRegistryEvent (entry.EventID);
						}
					}
					catch (FormatException)
					{
                        LogMgrError (RunErrorClass.EventLog, entry.EventID, "Event", CollectorError.LogUnexpectedEventFormat, 0, "Event is not in an expected format");
						LogUnexpectedEvent (entry);
                        throw new CollectorErrorException (CollectorError.LogUnexpectedEventFormat, 0, "Aborting collection. Event " + entry.EventID + " is not in an expected format.");
					}
					lSource.addEvent (stev);
					break;
				case 17055:
					#region Sql2000 events
					// Must be a Sql2000 event
					sub = entry.ReplacementStrings[0];
					try
					{
						isub = (int)Convert.ToInt32(sub);
					}
					catch (FormatException)
					{
                        LogMgrError (RunErrorClass.EventLog, entry.EventID, "Event", CollectorError.LogUnexpectedEventFormat, 0, "Event is not in an expected format");
						LogUnexpectedEvent (entry);
                        throw new CollectorErrorException (CollectorError.LogUnexpectedEventFormat, 0, "Aborting collection. Event 17055 is not in the expected format.");
					}
				switch (isub)
				{
					case 17147:
					case 17148:
						stev = new SqlServerEvent (inst_id, entry, isub);
						stev.level = lSource.hlevel;
						lSource.addEvent (stev);
						break;
					case 17104:
						stev = new SqlServerEvent (inst_id, entry, isub);
						stev.level = lSource.hlevel;
						lSource.addEvent (stev);
						break;
					case 17052:
						if (entry.ReplacementStrings[1].StartsWith("Microsoft SQL Server"))
						{
							stev = new SqlServerEvent (inst_id, entry, isub);
							lSource.addEvent (stev);
							lSource.hlevel = stev.level;
						}
						else if (entry.ReplacementStrings[1].StartsWith ("Recovery complete"))
						{ // Special case to get correct anchor for 17177
							stev = new SqlServerEvent (inst_id, entry, 3408);
							stev.level = lSource.hlevel;
							lSource.addEvent (stev);
						}
						break;
					case 19030: // trace started
					case 19031: // trace stopped
					case 19032: // trace stopped because of system shutdown
					case 17065:
					case 17066:
					case 17067:
					case 17308:
					case 17310:
					case 17311:
						stev = new SqlServerEvent (inst_id, entry, isub);
						stev.level = lSource.hlevel;
						lSource.addEvent (stev);
						break;
				}
					#endregion
					break;
				case 17052:
					#region Either Sql2000 17052 Or Sql2005 start event
					if (entry.ReplacementStrings.Length == 1)
					{
						if (entry.ReplacementStrings[0].Length > 64)
						{
							if ((entry.ReplacementStrings[0].IndexOf ("EXCEPTION_ACCESS_VIOLATION", 64) > 64) 
								|| (entry.ReplacementStrings[0].IndexOf ("access violation", 64) > 64)) 
							{
								stev = new SqlServerEvent (inst_id, entry, 0);
								lSource.addEvent (stev);
								break;
							}
						}
						if (entry.ReplacementStrings[0].StartsWith ("Error: "))
						{
							// This is Sql2000 17052 event (user forced event)
							// no need to store it
						}
						else if (entry.ReplacementStrings[0].StartsWith("Microsoft SQL Server"))
						{
							// This is Sql2005 start event
							stev = new SqlServerEvent (inst_id, entry);
							lSource.addEvent (stev);
							lSource.hlevel = H2ProductLevel.Sql2005;  // that includes Sql2008
							lSource.addRegistryEvent (entry.EventID);
						}
					}
					#endregion
					break;
					#region Sql2005 events
				case 0: // In Sql2000 it was 17052:Nothing
					if (entry.ReplacementStrings.Length == 1)
					{
						if (entry.ReplacementStrings[0].Length > 64)
						{
							if ((entry.ReplacementStrings[0].IndexOf ("EXCEPTION_ACCESS_VIOLATION", 64) > 64) 
								|| (entry.ReplacementStrings[0].IndexOf ("access violation", 64) > 64)) 
							{
								stev = new SqlServerEvent (inst_id, entry);
								lSource.addEvent (stev);
                                lSource.hlevel = H2ProductLevel.Sql2005;  // that includes Sql2008
							}
						}
					}
					lSource.addRegistryEvent (entry.EventID);
					break;
				case 17147:
				case 17148:
				case 3408:
				case 17104:
				case 19030: // trace started
				case 19031: // trace stopped
				case 19032: // trace stopped because of system shutdown
				case 17183: // attempting to cycle error log
				case 17184: // errorlog has been reinitialized
					//LogUnexpectedEvent (entry);
					stev = new SqlServerEvent (inst_id, entry);
					lSource.addEvent (stev);
					lSource.hlevel = H2ProductLevel.Sql2005;
					lSource.addRegistryEvent (entry.EventID);
					break;
				case 17065: // assertions & exceptions
				case 17066:
				case 17067:
				case 17308:
				case 17310:
				case 17311:
					inst_id = lSource.inst_id;
					if (entry.ReplacementStrings.Length == 1)
					{
						if (entry.ReplacementStrings[0].Length > 64)
						{
							if ((entry.ReplacementStrings[0].IndexOf ("EXCEPTION_ACCESS_VIOLATION", 64) > 64) 
								|| (entry.ReplacementStrings[0].IndexOf ("access violation", 64) > 64)) 
							{
								stev = new SqlServerEvent (inst_id, entry);
								lSource.addEvent (stev);
                                lSource.hlevel = H2ProductLevel.Sql2005;  // that includes Sql2008
							}
						}
					}
					lSource.addRegistryEvent (entry.EventID);
					break;
				default:
					lSource.addRegistryEvent (entry.EventID);
					break;
					#endregion
			}
		}

		#endregion

		#region Application Log Analysis functions
		void AnalyzeSqlOutages (LogSource source)
		{
			int i,
				len,
				count = 0;
			short inst_id;
			string version = "",
				cur_pid = "",
				last_pid = "",
			    // Candidate prefix is for storing info before confirming it's real start (not log recycle)
				candidate_pid = "",
				candidate_version = ""; 
			DateTime last_start = MIN_DATE,
				timeToCompare,
				timeToCompare2,
				UpgradeTime = MIN_DATE;
			SqlServerEvent stev,
				sql_cap = new SqlServerEvent (),
				new_cap = new SqlServerEvent (),
				last_104 = new SqlServerEvent (),
				last_stop = new SqlServerEvent (),
				candidate_cap = new SqlServerEvent (),
				candidate_104 = new SqlServerEvent ();
			bool SqlHistory = false, // if there is no log history it stays false
				IsCapUp = true,
				IsStarted = false,
				IsUpgrade = false
				;

			ChangeStatus ("  Analyzing SQL outages");
			UnitUpdateStatus (4);

			len = source.Count;
			inst_id = source.inst_id;

			#region History & Cap
			if (!m_log_arr[1].NoHistory) 
			{
				if (GetSqlCap (inst_id, ref sql_cap))
				{
					SqlHistory = true;
					last_start = GetLastSqlStart (inst_id);
					cur_pid = sql_cap.pid;
					new_cap = sql_cap;
					if (sql_cap.EventID == 17055)
					{	// Sql2000
						if (sql_cap.SubEventID == 17104)
						{
							last_104 = sql_cap;
						}
						else if (sql_cap.SubEventID == 17148 || sql_cap.SubEventID == 17147)
						{
							IsCapUp = false;
						}
					}
					else
					{	//Sql2005
						if (sql_cap.EventID == 17104)
						{
							last_104 = sql_cap;
						}
						else if (sql_cap.EventID == 17148 || sql_cap.EventID == 17147)
						{
							IsCapUp = false;
						}
					}
				}
				else
				{
					ChangeStatus ("  There is log history recorded, but no sql cap found. It may indicate truncated event log.", StatusEventType.WARNING);
				}
			}
			#endregion

			for (i=0; i<len; i++ )
			{
				stev = (SqlServerEvent)source[i];

				if (stev.level == H2ProductLevel.PreSql2000SP3)
				{	// Just ignore it
					continue;
				}
				if (stev.level == H2ProductLevel.UNKNOWN || stev.level == H2ProductLevel.Sql2000SP3)
				{
					#region Sql2000
					switch (stev.EventID)
					{
						case 17055:
						switch (stev.SubEventID)
						{
							case 17052: //start (version)
								candidate_version = stev.pid;
								break;
							case 17104: //pid
								++count;
								candidate_pid = stev.pid;
								candidate_cap = stev;
								candidate_104 = stev;
								break;
							case 3408: //Recovery complete
								last_104.TimeReferenced = stev.TimeGenerated;
								break;
							case 17147: //stop
							case 17148:
							case 6006:
								last_stop = stev;
								break;
						}
							break;
						case 17176: // startup (last heartbeat)
							++count;
							IsStarted = true;

							last_pid = cur_pid;

							if (IsCapUp)
							{
								if (last_start != MIN_DATE) StoreSqlOutage 
																(inst_id, last_start, stev.TimeReferenced, 1, last_pid, version);
								StoreSqlOutage (inst_id, stev.TimeReferenced, stev.TimeGenerated, 0, last_pid, version);
							}
							else 
							{ // 1-time event if cap event was 'stop' 
								IsCapUp = true;
								StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeGenerated, 0, last_pid, version);
							}

							version = candidate_version;
							cur_pid = candidate_pid;
							new_cap = candidate_cap;
							last_104 = candidate_104;
							last_start = stev.TimeGenerated;
							break;
						case 17177: // midnight
							++count;
							IsStarted = true;	

							if (stev.pid != cur_pid)
							{
								#region 17177 pid doesn't match cur_pid
								if (cur_pid == "" && last_start == MIN_DATE)
								{	// this must be the first 17177 in the log with no preceding start events
									cur_pid = stev.pid;
									last_start = stev.TimeReferenced;
									if (!SqlHistory)
									{   // first time we go through log
									}
									else //there is history, cap exists
									{   
										//timeToCompare = sql_cap.EventID==17177 ? sql_cap.TimeReferenced : sql_cap.TimeGenerated;
										timeToCompare = sql_cap.TimeReferenced;
										if (timeToCompare > stev.TimeReferenced)
										{	// pathology 901 - referenced pid starts earlier than the cap
											LogUnexpectedSqlEvent (sql_cap);
											LogUnexpectedSqlEvent (stev);
                                            throw new CollectorErrorException (CollectorError.LogPidDoesntMatchCap, 0, "SQL_outage_analyze - log inconsistency: the first 17177 doesn't match pid of the cap event and references to earlier time than the cap.TimeGenerated");
										}
										if (sql_cap.pid != stev.pid)
										{	
											if (m_log_arr[1].IsGap)
											{	// it's probably OK to have unmatched pids if there is a gap
												StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
												StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
											}
											else
											{
												// pathology 903 - different pids, no start event in between
												LogUnexpectedSqlEvent (sql_cap);
												LogUnexpectedSqlEvent (stev);
                                                throw new CollectorErrorException (CollectorError.LogPidDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: 17177 doesn't match the previous pid (no history gap)");
											}
										}
										else // pid is the same - still have to check time
										{
											timeToCompare = sql_cap.TimeReferenced;
											if (stev.TimeReferenced != timeToCompare)
											{	// it's probably OK if there is a gap
												if (m_log_arr[1].IsGap)
												{
													StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
													StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
												}
												else
												{	// pathology 902 - referenced has different timestamp
													LogUnexpectedSqlEvent (sql_cap);
													LogUnexpectedSqlEvent (stev);
                                                    throw new CollectorErrorException (CollectorError.LogTimeDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: the first 17177 matches the cap's pid but has a different timestamp (no history gap)");
												}
											}
											// else 17177 references the cap - just continue
										}
									}

								}
								else	// this is not the first 17177 pid but it doesn't match the previous one
								{
									if ((new_cap.EventID == 17177 && stev.TimeReferenced < new_cap.TimeReferenced)
										|| (new_cap.EventID != 17177 && stev.TimeReferenced < new_cap.TimeGenerated))
									{	// pathology 901 
										LogUnexpectedSqlEvent (sql_cap);
										LogUnexpectedSqlEvent (new_cap);
										LogUnexpectedSqlEvent (stev);
                                        throw new CollectorErrorException (CollectorError.LogPidDoesntMatchCap, 0, "SQL_outage_analyze - log inconsistency: the first 17177 doesn't match pid of the cap event and references to earlier time than the cap.TimeGenerated");
									}
									if (m_log_arr[1].IsGap)
									{	// it's probably OK to have unmatched pids if there is a gap
										StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
										StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
									}
									else
									{	// pathology 903 - different pids, no start event in between
										LogUnexpectedSqlEvent (sql_cap);
										LogUnexpectedSqlEvent (stev);
                                        throw new CollectorErrorException (CollectorError.LogPidDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: 17177 doesn't match the previous pid (no history gap)");
									}

								}
								#endregion
							}
							else	// if pid is the same we only check timestamps
							{
								timeToCompare = new_cap.TimeReferenced;
								timeToCompare2 = new_cap.TimeReferenced.AddSeconds (5);
								if (
									(new_cap.SubEventID == 17104 && (stev.TimeReferenced < new_cap.TimeGenerated || stev.TimeReferenced > timeToCompare2))
									|| (new_cap.SubEventID != 17104 && stev.TimeReferenced < new_cap.TimeReferenced))
								{	// pathology 906 
									LogUnexpectedSqlEvent (new_cap);
									LogUnexpectedSqlEvent (stev);
                                    throw new CollectorErrorException (CollectorError.LogReferencedTimeDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: the first 17177 matches pid of the cap event, but references to a different time");
								}
							
								TimeSpan ts = stev.TimeReferenced - timeToCompare;
								if (ts.TotalSeconds > 60)
								{
									if (m_log_arr[1].IsGap)
									{	// it's probably OK if there is a gap
										StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
										StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
									}
									else
									{	// pathology 904 - same pids, different timestamps, no start event in between
										LogUnexpectedSqlEvent (new_cap);
										LogUnexpectedSqlEvent (stev);
                                        throw new CollectorErrorException (CollectorError.LogReferencedTimeChanged, 0, "SQL_outage_analyze - log inconsistency: referenced time changed (same pid; no history gap)");
									}
								}
							}
							new_cap = stev;
							break;
					}
					#endregion
				}
				else if (stev.level == H2ProductLevel.Sql2005)
				{
					#region Sql2005
					switch (stev.EventID)
					{
						case 17052: //start (version)
							candidate_version = stev.pid;
							if (version != "" && candidate_version != version)
							{
								IsUpgrade = true;
								if (last_start < last_stop.TimeGenerated)
								{
									// record uptime using last_stop
									UpgradeTime = last_stop.TimeGenerated;
								}
								else
								{
									// record uptime using new_cap
									UpgradeTime = new_cap.TimeGenerated;
								}
								StoreSqlOutage (inst_id, last_start, UpgradeTime, 1, cur_pid, version);
								version = candidate_version;
							}
							break;
						case 17104: //pid
							++count;
							candidate_pid = stev.pid;
							candidate_cap = stev;
							candidate_104 = stev;
							break;
						case 3408: //Recovery complete
							last_104.TimeReferenced = stev.TimeGenerated;
							break;
						case 17147: //stop
						case 17148:
						case 6006:
							last_stop = stev;
							break;
						case 17176: // startup (last heartbeat)
							++count;

							IsStarted = true;	

							last_pid = cur_pid;

							if (IsUpgrade)
							{
								StoreSqlOutage (inst_id, UpgradeTime, stev.TimeGenerated, 0, last_pid, version);
								IsUpgrade = false;
							}
							else
							{
								if (IsCapUp)
								{
									if (last_start != MIN_DATE) 
									{
										StoreSqlOutage (inst_id, last_start, stev.TimeReferenced, 1, last_pid, version);
									}
								}
								else
								{ // 1-time event if cap event was 'stop' 
									IsCapUp = true;
								}
								StoreSqlOutage (inst_id, stev.TimeReferenced, stev.TimeGenerated, 0, last_pid, version);
							}
							
							version = candidate_version;
							cur_pid = candidate_pid;
							new_cap = candidate_cap;
							last_104 = candidate_104;
							last_start = stev.TimeGenerated;
							break;
						case 17177: // midnight
							++count;
							IsStarted = true;	
							if (stev.pid != cur_pid)
							{
								#region 17177 pid doesn't match cur_pid
								if (cur_pid == "" && last_start == MIN_DATE)
								{	// this must be the first 17177 in the log with no preceding start events
									cur_pid = stev.pid;
									last_start = stev.TimeReferenced;
									if (!SqlHistory)
									{   // first time we go through log
									}
									else //there is history, cap exists
									{   
										timeToCompare = sql_cap.TimeReferenced;
										if (timeToCompare > stev.TimeReferenced)
										{	// pathology 901 - referenced pid starts earlier than the cap
											LogUnexpectedSqlEvent (sql_cap);
											LogUnexpectedSqlEvent (stev);
                                            throw new CollectorErrorException (CollectorError.LogPidDoesntMatchCap, 0, "SQL_outage_analyze - log inconsistency: the first 17177 doesn't match pid of the cap event and references to earlier time than the cap.TimeGenerated");
										}
										if (sql_cap.pid != stev.pid)
										{	
											if (m_log_arr[1].IsGap)
											{	// it's probably OK to have unmatched pids if there is a gap
												StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
												StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
											}
											else
											{
												// pathology 903 - different pids, no start event in between
												LogUnexpectedSqlEvent (sql_cap);
												LogUnexpectedSqlEvent (stev);
                                                throw new CollectorErrorException (CollectorError.LogPidDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: 17177 doesn't match the previous pid (no history gap)");
											}
										}
										else // pid is the same - still have to check time
										{
											timeToCompare = sql_cap.TimeReferenced;
											if (stev.TimeReferenced != timeToCompare)
											{	// it's probably OK if there is a gap
												if (m_log_arr[1].IsGap)
												{
													StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
													StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
												}
												else
												{	// pathology 902 - referenced has different timestamp
													LogUnexpectedSqlEvent (sql_cap);
													LogUnexpectedSqlEvent (stev);
                                                    throw new CollectorErrorException (CollectorError.LogTimeDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: the first 17177 matches the cap's pid but has a different timestamp (no history gap)");
												}
											}
											// else 17177 references the cap - just continue
										}
									}

								}
								else	// this is not the first 17177 pid but it doesn't match the previous one
								{
									if ((new_cap.EventID == 17177 && stev.TimeReferenced < new_cap.TimeReferenced)
										|| (new_cap.EventID != 17177 && stev.TimeReferenced < new_cap.TimeGenerated))
									{	// pathology 901 
										LogUnexpectedSqlEvent (sql_cap);
										LogUnexpectedSqlEvent (new_cap);
										LogUnexpectedSqlEvent (stev);
                                        throw new CollectorErrorException (CollectorError.LogPidDoesntMatchCap, 0, "SQL_outage_analyze - log inconsistency: the first 17177 doesn't match pid of the cap event and references to earlier time than the cap.TimeGenerated");
									}
									if (m_log_arr[1].IsGap)
									{	// it's probably OK to have unmatched pids if there is a gap
										StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
										StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
									}
									else
									{	// pathology 903 - different pids, no start event in between
										LogUnexpectedSqlEvent (sql_cap);
										LogUnexpectedSqlEvent (stev);
                                        throw new CollectorErrorException (CollectorError.LogPidDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: 17177 doesn't match the previous pid (no history gap)");
									}

								}
								#endregion
							}
							else	// if pid is the same we only check timestamps
							{
								timeToCompare = new_cap.TimeReferenced.AddSeconds (-3);	// allow 3 seconds for mismatch
								timeToCompare2 = new_cap.TimeReferenced.AddSeconds (5);	
								if (
									(new_cap.EventID == 17104 && (stev.TimeReferenced < new_cap.TimeGenerated || stev.TimeReferenced > timeToCompare2))
									|| (new_cap.EventID != 17104 && stev.TimeReferenced < new_cap.TimeReferenced))
								{	// pathology 906 
									LogUnexpectedSqlEvent (new_cap);
									LogUnexpectedSqlEvent (stev);
                                    throw new CollectorErrorException (CollectorError.LogReferencedTimeDoesntMatch, 0, "SQL_outage_analyze - log inconsistency: the first 17177 matches pid of the cap event, but references to a different time");
								}
							
								TimeSpan ts = stev.TimeReferenced - timeToCompare;
								if (ts.TotalSeconds > 60)
								{
									if (m_log_arr[1].IsGap)
									{	// it's probably OK if there is a gap
										StoreSqlOutage (inst_id, sql_cap.TimeReferenced, sql_cap.TimeGenerated, 1, sql_cap.pid, "");
										StoreSqlOutage (inst_id, sql_cap.TimeGenerated, stev.TimeReferenced, -1, stev.pid, version);
									}
									else
									{	// pathology 904 - same pids, different timestamps, no start event in between
										LogUnexpectedSqlEvent (new_cap);
										LogUnexpectedSqlEvent (stev);
                                        throw new CollectorErrorException (CollectorError.LogReferencedTimeChanged, 0, "SQL_outage_analyze - log inconsistency: referenced time changed (same pid; no history gap)");
									}
								}
							}
							new_cap = stev;
							break;
					}
					#endregion
				}

			}

			if (IsStarted && (last_stop.TimeGenerated > new_cap.TimeGenerated))
			{
				last_stop.pid = cur_pid;
				last_stop.TimeReferenced = last_start;
				new_cap = last_stop;
				StoreSqlOutage (inst_id, new_cap.TimeReferenced, new_cap.TimeGenerated, 1, new_cap.pid, version);
			}


			if (new_cap.EventID != 0) StoreSqlCap (new_cap);
		}

		void AnalyzeTraceHistory (LogSource source)
		{
			int idx,
				i,
				eventID,
				count = 0;
			short trace_id;
			DateTime time;
			SqlServerEvent stev;
			SortedList sl = new SortedList (4);

			ChangeStatus ("  Analyzing trace history");
			UnitUpdateStatus (5);

			for (i=0; i<source.Count; i++)
			{
				eventID = 0;
				stev = (SqlServerEvent)source[i];

				if (source.level == H2ProductLevel.Sql2000SP3) 
				{
					if (stev.EventID == 17055) 
					{
						eventID = stev.SubEventID;
					}
					else
					{
						continue;
					}
				}
				else if (source.level == H2ProductLevel.Sql2005)
				{
					eventID = stev.EventID;
				}

				if (eventID == 19030)
				{ // start
					if (sl.ContainsKey (stev.pid)) 
					{ // problem - stop missed
						idx = sl.IndexOfKey (stev.pid);
						trace_id = Convert.ToInt16 (stev.pid);
						time = (DateTime)sl.GetByIndex (idx);
						ChangeStatus (time.ToString (), StatusEventType.INFO);
						ChangeStatus ("Repeating event 19030 for trace_id="+stev.pid, StatusEventType.WARNING);
						StoreTrace (stev.inst_id, trace_id, time, MIN_DATE);
						sl.RemoveAt (idx);
					}
					sl.Add (stev.pid, stev.TimeGenerated);
				}
				else if (eventID == 19031)
				{ // stop
					if (sl.ContainsKey (stev.pid))
					{ // store trace
						idx = sl.IndexOfKey (stev.pid);
						time = (DateTime)sl.GetByIndex (idx);
						trace_id = Convert.ToInt16 (stev.pid);
						StoreTrace (stev.inst_id, trace_id, time, stev.TimeGenerated);
						++count;
						sl.RemoveAt (idx);
					}
					else 
					{ // trace could be started before cutoff
						trace_id = Convert.ToInt16 (stev.pid);
						StoreTrace (stev.inst_id, trace_id, MIN_DATE, stev.TimeGenerated);
						++count;
					}
				}
				else if (eventID == 19032)
				{ // stop - shutdown
					if (sl.ContainsKey (stev.pid))
					{ // store trace
						idx = sl.IndexOfKey (stev.pid);
						time = (DateTime)sl.GetByIndex (idx);
						trace_id = Convert.ToInt16 (stev.pid);
						StoreTrace (stev.inst_id, trace_id, time, stev.TimeGenerated);
						++count;
						sl.RemoveAt (idx);
					}
					else 
					{ // trace could be started before cutoff
						trace_id = Convert.ToInt16 (stev.pid);
						StoreTrace (stev.inst_id, trace_id, MIN_DATE, stev.TimeGenerated);
						++count;
					}
				}
			}

			// running traces should remain in the collection
			for (i=0; i<sl.Count; i++)
			{
				trace_id = Convert.ToInt16 ((string)sl.GetKey (i));
				time = (DateTime)sl.GetByIndex (i);
				StoreTrace (source.inst_id, trace_id, time, MIN_DATE);
			}
		}

		void AnalyzeAV (LogSource source)
		{
			switch (source.level)
			{
				case H2ProductLevel.Sql2000SP3:
					AnalyzeAV2000 (source);
					break;
				case H2ProductLevel.Sql2005:
                case H2ProductLevel.Sql2008:
					AnalyzeAV2005 (source);
					break;
			}
		}

		void AnalyzeAV2000 (LogSource source)
		{
			int i,
				count = 0;
			SqlServerEvent stev;

			ChangeStatus ("  Analyzing AV/assertion history");
			UnitUpdateStatus (6);

			for (i=0; i<source.Count; i++)
			{
				stev = (SqlServerEvent)source[i];

				if ((stev.EventID == 17052 && stev.SubEventID == 0)	|| 
					(stev.EventID == 17055 
					&& (stev.SubEventID == 17065 || stev.SubEventID == 17066 || stev.SubEventID == 17067 
					|| stev.SubEventID == 17308 || stev.SubEventID == 17310 || stev.SubEventID == 17311)))
				{
						StoreAV (stev);
						++count;
				}
			}
		}

		void AnalyzeAV2005 (LogSource source)
		{
			int i,
				count = 0;
			SqlServerEvent stev;

			ChangeStatus ("  Analyzing AV/assertion history");
			UnitUpdateStatus (6);

			for (i=0; i<source.Count; i++)
			{
				stev = (SqlServerEvent)source[i];

				if (stev.EventID == 0 || stev.EventID == 17065 || stev.EventID == 17066 || stev.EventID == 17067 
					|| stev.EventID == 17308 || stev.EventID == 17310 || stev.EventID == 17311)
				{
					StoreAV (stev);
					++count;
				}
			}
		}

		void AnalyzeWatson ()
		{
			int count = 0;

			ChangeStatus ("  Analyzing Watson history");
			UnitUpdateStatus (7);

			foreach (AppLogEvent stav in m_app_event_array)
			{
				if (stav.EventID == 1001 || stav.EventID == 1000 || stav.EventID == 1002 || stav.EventID == 1004
					|| stav.EventID == 5000 || stav.EventID == 5001) 
				{
					StoreWatson (stav);
					++count;
				}
			}
		}

		#endregion

		#region Store functions
		void StoreWatson (AppLogEvent a_event)
		{
            SqlCommand cmd = new SqlCommand ();
            cmd.CommandText = 
@"Insert dbo.c_watson_event (run_id,srv_id,eventID,source,timegenerated,info) 
Values (@run_id,@srv_id,@eventID,@source,@timegenerated,@info) ";
            cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
            cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            cmd.Parameters.AddWithValue ("@eventID", a_event.EventID);
            cmd.Parameters.AddWithValue ("@source", a_event.Source);
            cmd.Parameters.AddWithValue ("@timegenerated", a_event.TimeGenerated);
            cmd.Parameters.AddWithValue ("@info", a_event.info);

			UnitAddData (cmd);
		}

		void StoreAV (SqlServerEvent a_event)
		{
            SqlCommand cmd = new SqlCommand ();
            cmd.CommandText = 
@"Insert dbo.c_av (run_id,srv_id,inst_id,event_id,subevent_id,timegenerated,info) 
Values (@run_id,@srv_id,@inst_id,@event_id,@subevent_id,@timegenerated,@info) ";
            cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
            cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            cmd.Parameters.AddWithValue ("@inst_id", a_event.inst_id);
            cmd.Parameters.AddWithValue ("@event_id", a_event.EventID);
            cmd.Parameters.AddWithValue ("@subevent_id", a_event.SubEventID);
            cmd.Parameters.AddWithValue ("@timegenerated", a_event.TimeGenerated);
            cmd.Parameters.AddWithValue ("@info", a_event.pid);

            UnitAddData (cmd);
		}

        void StoreTrace (short a_inst_id, short a_id, DateTime a_start, DateTime a_finish)
        {
            SqlCommand cmd = new SqlCommand ();
            cmd.CommandText = 
@"Insert dbo.c_trace_history (run_id, srv_id, inst_id, trace_id, start, finish) 
Values (@run_id,@srv_id,@inst_id,@trace_id,@start,@finish) ";
            cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
            cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            cmd.Parameters.AddWithValue ("@inst_id", a_inst_id);
            cmd.Parameters.AddWithValue ("@trace_id", a_id);
            if (a_start != MIN_DATE)
            {
                cmd.Parameters.AddWithValue ("@start", a_start);
            }
            else
            {
                cmd.Parameters.AddWithValue ("@start", System.DBNull.Value);
            }
            if (a_finish != MIN_DATE)
            {
                cmd.Parameters.AddWithValue ("@finish", a_start);
            }
            else
            {
                cmd.Parameters.AddWithValue ("@finish", System.DBNull.Value);
            }

            UnitAddData (cmd);
        }

        void StoreSqlOutage (short a_inst_id, DateTime a_start, DateTime a_finish, int a_state, string a_pid, string a_ver)
        {

            if (a_start == MIN_DATE)
            {
                ChangeStatus ("SQL outage start date is MIN_DATE", StatusEventType.DEBUG);
            }

            SqlCommand cmd = new SqlCommand ();
            cmd.CommandText = 
@"Insert dbo.c_sql_outage (run_id, srv_id, inst_id, start, finish, state, pid, version) 
Values (@run_id,@srv_id,@inst_id,@start,@finish,@state,@pid,@version) ";
            cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
            cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            cmd.Parameters.AddWithValue ("@inst_id", a_inst_id);
            cmd.Parameters.AddWithValue ("@start", a_start);
            cmd.Parameters.AddWithValue ("@finish", a_finish);
            cmd.Parameters.AddWithValue ("@state", a_state);
            cmd.Parameters.AddWithValue ("@pid", a_pid);
            cmd.Parameters.AddWithValue ("@version", a_ver);

            UnitAddData (cmd);
        }

		void StoreOSoutage (DateTime a_start, DateTime a_finish, int a_state, string a_ver, int a_dirty)
		{
            SqlCommand cmd = new SqlCommand ();
            cmd.CommandText = 
@"Insert dbo.c_os_outage (run_id, srv_id, start, finish, IsDirty, state, version) 
Values (@run_id,@srv_id,@start,@finish,@IsDirty,@state,@version) ";
            cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
            cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            cmd.Parameters.AddWithValue ("@start", a_start);
            cmd.Parameters.AddWithValue ("@finish", a_finish);
            cmd.Parameters.AddWithValue ("@IsDirty", a_dirty);
            cmd.Parameters.AddWithValue ("@state", a_state);
            cmd.Parameters.AddWithValue ("@version", a_ver);

            UnitAddData (cmd);
		}

		void StoreSqlCap (SqlServerEvent a_event)
		{
			if (a_event.EventID == 0) return;
			
            SqlCommand cmd = new SqlCommand ();
            cmd.CommandText = 
@"Insert dbo.c_sql_log_cap (run_id,srv_id,inst_id,event_id,subevent_id,pid,timegenerated,timereferenced) 
Values (@run_id,@srv_id,@inst_id,@event_id,@subevent_id,@pid,@timegenerated,@timereferenced) ";
            cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
            cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            cmd.Parameters.AddWithValue ("@inst_id", a_event.inst_id);
            cmd.Parameters.AddWithValue ("@event_id", a_event.EventID);
            cmd.Parameters.AddWithValue ("@subevent_id", a_event.SubEventID);
            cmd.Parameters.AddWithValue ("@pid", a_event.pid);
            cmd.Parameters.AddWithValue ("@timegenerated", a_event.TimeGenerated);
            cmd.Parameters.AddWithValue ("@timereferenced", a_event.TimeReferenced);

            UnitAddData (cmd);
		}

		void StoreLogHistory (short a_type, LogHistoryRecord a_lr)
		{
            SqlCommand cmd = new SqlCommand ();
            cmd.CommandText = 
@"Insert dbo.c_log_history (run_id,srv_id,type,start,start_index,finish,finish_index,e_count,IsHistory,IsGap,prev_run_id) 
Values (@run_id,@srv_id,@type,@start,@start_index,@finish,@finish_index,@e_count,@IsHistory,@IsGap,@prev_run_id) ";
            cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
            cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            cmd.Parameters.AddWithValue ("@type", a_type);
            cmd.Parameters.AddWithValue ("@start", a_lr.start.TimeGenerated);
            cmd.Parameters.AddWithValue ("@start_index", a_lr.start.index);
            cmd.Parameters.AddWithValue ("@finish", a_lr.finish.TimeGenerated);
            cmd.Parameters.AddWithValue ("@finish_index", a_lr.finish.index);
            cmd.Parameters.AddWithValue ("@e_count", a_lr.count);
            cmd.Parameters.AddWithValue ("@IsHistory", !a_lr.NoHistory);
            cmd.Parameters.AddWithValue ("@IsGap", a_lr.IsGap);
            cmd.Parameters.AddWithValue ("@prev_run_id", a_lr.prev_run_id);
            
            UnitAddData (cmd);
		}

		void StoreEventRegistry (LogSource lSource)
		{
			int q;

            foreach (int eventID in lSource.EventRegistry.GetKeyList ())
			{
				q = (int)lSource.EventRegistry[eventID];

                SqlCommand cmd = new SqlCommand ();
                cmd.CommandText = "Insert dbo.c_event_counts (run_id, srv_id, inst_id, event_id, q) Values (@run_id,@srv_id,@inst_id,@event_id,@q) ";
                cmd.Parameters.AddWithValue ("@run_id", m_rd.run_id);
                cmd.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
                cmd.Parameters.AddWithValue ("@inst_id", lSource.inst_id);
                cmd.Parameters.AddWithValue ("@event_id", eventID);
                cmd.Parameters.AddWithValue ("@q", q);
                
                UnitAddData (cmd);
			}
		}


		#endregion

		#region Get functions (retrieve data from DB)
		void GetCutoffDate (int type)
		{
            SqlCommand command = new SqlCommand (
                "Select finish, finish_index, run_id From dbo.c_last_log Where srv_id=@srv_id and type=@type ",
                m_repository.SQLConnection);
            command.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            command.Parameters.AddWithValue ("@type", type);

            SqlDataReader dataReader = command.ExecuteReader ();
			try
			{
				if (dataReader.Read ())
				{
					m_log_arr[type].cutoff.TimeGenerated = (DateTime)dataReader.GetDateTime (0);
					m_log_arr[type].cutoff.index = (int)dataReader.GetInt32 (1);
					m_log_arr[type].prev_run_id = (int)dataReader.GetInt32 (2);
					m_log_arr[type].NoHistory = false;
				}
				else
				{
					m_log_arr[type].NoHistory = true;
				}
			}
			finally
			{
				dataReader.Close ();
			}
		}
		
		bool GetSqlCap (short a_inst_id, ref SqlServerEvent sql_cap)
		{
			int event_id,
				subevent_id;

            SqlCommand command = new SqlCommand (
"Select event_id, subevent_id, pid, timegenerated, timereferenced From dbo.c_sql_log_cap Where srv_id=@srv_id and inst_id=@inst_id ",
                m_repository.SQLConnection);
            command.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            command.Parameters.AddWithValue ("@inst_id", a_inst_id);
            
			SqlDataReader dataReader = command.ExecuteReader ();

			try
			{
				if (!dataReader.Read ()) return false;

				sql_cap.inst_id = a_inst_id; 
				event_id = (int)dataReader.GetInt32 (0);
				subevent_id = (int)dataReader.GetInt32 (1);
			
				sql_cap.EventID = event_id;
				sql_cap.SubEventID = subevent_id;
				sql_cap.pid = (string)dataReader.GetString (2);
				sql_cap.TimeGenerated = (DateTime)dataReader.GetDateTime (3);
				sql_cap.TimeReferenced = (DateTime)dataReader.GetDateTime (4);
			}
			finally
			{
				dataReader.Close ();
			}
			
			return true;
		}

		DateTime GetLastSqlStart (short a_inst_id)
		{
            SqlCommand command = new SqlCommand (
                "Select IsNULL(MAX(finish),0) From dbo.c_sql_outage Where srv_id=@srv_id and inst_id=@inst_id and state=0 ",
                m_repository.SQLConnection);
            command.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);
            command.Parameters.AddWithValue ("@inst_id", a_inst_id);

            DateTime time = (DateTime)command.ExecuteScalar ();

			if (time < MIN_DATE) time = MIN_DATE;  

			return time;
		}

		DateTime GetLastBootTime ()
		{
            SqlCommand command = new SqlCommand (
                "Select IsNULL(MAX(finish),0) From dbo.c_os_outage Where srv_id=@srv_id ",
                m_repository.SQLConnection);
            command.Parameters.AddWithValue ("@srv_id", m_rd.srv_id);

            DateTime time = (DateTime)command.ExecuteScalar ();

            if (time < MIN_DATE) time = MIN_DATE;  

			return time;
		}
		#endregion
 
	}
	
}

